package com.xuexiang.yoyo.runenable;

import android.content.Context;
import android.os.Build;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;

import androidx.annotation.RequiresApi;

import java.security.Key;
import java.time.Duration;
import java.time.LocalDateTime;

import javax.crypto.Cipher;

@RequiresApi(api = Build.VERSION_CODES.O)
public class RunEnable {
    private static final String TAG = "RunEnable";

    private static LocalDateTime stopDate = LocalDateTime.now();

    /*
    * 要获取APP最后运行时间之前，必须先执行判断APP是否可以运行的函数，然后就可以获得的APP最后始能时间
    * */
    public static LocalDateTime getStopDate() {
        return stopDate;
    }

    public static int getEnableDays() {
        Duration dur= Duration.between(LocalDateTime.now(),  stopDate);
        return (int) dur.toDays();
    }

    private static byte[] hexStr2Bytes(String hexStr) {
        String tmpStr = hexStr;
        int len = hexStr.length();
        if ((len % 2) != 0) {
            tmpStr = "0" + tmpStr;
            len = tmpStr.length();
        }
        byte[] data = new byte[len / 2];
        for (int i = 0; i < data.length; i++) {
            String tmp = tmpStr.substring(i * 2, i * 2 + 2);
            data[i] = (byte) Integer.parseInt(tmp, 16);
        }
        return data;
    }

    private static Key getKey(byte[] arrBTmp) throws Exception {
        // 创建一个空的8位字节数组（默认值为0）
        byte[] arrB = new byte[8];

        // 将原始字节数组转换为8位
        for (int i = 0; i < arrBTmp.length && i < arrB.length; i++) {
            arrB[i] = arrBTmp[i];
        }

        // 生成密钥
        Key key = new javax.crypto.spec.SecretKeySpec(arrB, "DES");

        return key;
    }

    /*
     *  使用提供的UUID来计算进行token生成时候需要的key
     *  16字节UUID按照下面的方式分成2组，其中组1作为初始密钥，组2作为DataBlock，然后产生计算token需要的key
     *  组1：字节 1，2，5，6， 7， 8，11，12
     *  组2：字节 0，3，4，9，10，13，14，15
     *
     *  参数：String uuid
     * */
    private static String calcKey(String uuid) {
        byte[] uuidB = hexStr2Bytes(uuid);
        byte[] data1 = new byte[8];
        byte[] data2 = new byte[8];
        data2[0] = uuidB[0];
        data1[0] = uuidB[1];
        data1[1] = uuidB[2];
        data2[1] = uuidB[3];
        data2[2] = uuidB[4];
        data1[2] = uuidB[5];
        data1[3] = uuidB[6];
        data1[4] = uuidB[7];
        data1[5] = uuidB[8];
        data2[3] = uuidB[9];
        data2[4] = uuidB[10];
        data1[6] = uuidB[11];
        data1[7] = uuidB[12];
        data2[5] = uuidB[13];
        data2[6] = uuidB[14];

        data2[7] = uuidB[15];
        Cipher mEncryptCipher = null;
        try {
            mEncryptCipher = Cipher.getInstance("DES/ECB/NoPadding");
            Key key = getKey(data1);
            mEncryptCipher.init(Cipher.ENCRYPT_MODE, key);
            byte[] meterKey = mEncryptCipher.doFinal(data2);
            byte[] kb = key.getEncoded();
            for (int i = 0; i < meterKey.length; i++) {
                meterKey[i] = (byte) (meterKey[i] ^ data2[i] ^ kb[i]);
            }
            StringBuilder strKey = new StringBuilder();
            for (byte bb : meterKey) {
                strKey.append(String.format("%02X", bb));
            }
            return strKey.toString();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    private static String calcKey(String uuid, String androidId) {
        byte[] uuidB = hexStr2Bytes(uuid);
        byte[] data1 = new byte[8];
        byte[] data2 = new byte[8];
        data2[0] = uuidB[0];
        data1[0] = uuidB[1];
        data1[1] = uuidB[2];
        data2[1] = uuidB[3];
        data2[2] = uuidB[4];
        data1[2] = uuidB[5];
        data1[3] = uuidB[6];
        data1[4] = uuidB[7];
        data1[5] = uuidB[8];
        data2[3] = uuidB[9];
        data2[4] = uuidB[10];
        data1[6] = uuidB[11];
        data1[7] = uuidB[12];
        data2[5] = uuidB[13];
        data2[6] = uuidB[14];

        data2[7] = uuidB[15];

        byte[] aidB = hexStr2Bytes(androidId);
        // 使用andoidID对两组数据进行异或后，获得新的data
        for (int i = 0; i < 8; i++) {
            data1[i] = (byte)((data1[i] ^ aidB[i]) & 0xFF);
            data2[i] = (byte)((data2[i] ^ aidB[i]) & 0xFF);
        }

        Cipher mEncryptCipher = null;
        try {
            mEncryptCipher = Cipher.getInstance("DES/ECB/NoPadding");
            Key key = getKey(data1);
            mEncryptCipher.init(Cipher.ENCRYPT_MODE, key);
            byte[] meterKey = mEncryptCipher.doFinal(data2);
            byte[] kb = key.getEncoded();
            for (int i = 0; i < meterKey.length; i++) {
                meterKey[i] = (byte) (meterKey[i] ^ data2[i] ^ kb[i]);
            }
            StringBuilder strKey = new StringBuilder();
            for (byte bb : meterKey) {
                strKey.append(String.format("%02X", bb));
            }
            return strKey.toString();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    public static boolean decode(String strUuid, String tokenStr) {
        if (TextUtils.isEmpty(strUuid)) {
            Log.e(TAG, "decode: " + "uuid is Empty");
            return false;
        }
        BaseToken bToken = new BaseToken(WorkType.STA, false);
        int tClass = (int) bToken.GetClassNum(tokenStr);
        stopDate = null;
        if (-1 == tClass) {
            Log.e(TAG, "decode: " + "Token Error -- subClass is -1");
            return false;
        } else {
            if (tClass == 0) {
                String key = calcKey(strUuid);
                if (key != null) {
                    CreditToken myToken = new CreditToken(WorkType.STA, false);
                    if (!myToken.DecodeToken(tokenStr, key)) {
                        Log.e(TAG, "decode: " + "Token Error -- DecodeToken is FALSE");
                        return false;
                    } else {
                        Log.d(TAG, "decode: Amount  : " + myToken.get_amount());
                        Log.d(TAG, "decode: TID     : " + myToken.Tid);
                        Log.d(TAG, "decode: Random  : " + myToken.RandomNum);
                        Log.d(TAG, "decode: DateTime: " + myToken.IssueTime);
                        Log.d(TAG, "decode: subClass: " + myToken.TSubClassNum);

                        stopDate = myToken.IssueTime.plusDays((int) myToken.get_amount());
                        return true;
                    }
                } else {
                    Log.e(TAG, "decode: " + "UUID is error");
                    return false;
                }
            } else {
                Log.e(TAG, "decode: " + "Token Error -- subclass is error");
                return false;
            }
        }
    }


    public static LocalDateTime getTokenTime(String strUuid,String androidId,String tokenStr){
        CreditToken myToken = new CreditToken(WorkType.STA, false);
        String key = calcKey(strUuid, androidId);
        myToken.DecodeToken(tokenStr,key);
        return myToken.IssueTime;
    }

    public static boolean decode(String strUuid, String androidId, String tokenStr) {
        if (TextUtils.isEmpty(strUuid)) {
            Log.e(TAG, "decode: " + "uuid is Empty");
            return false;
        }
        BaseToken bToken = new BaseToken(WorkType.STA, false);
        int tClass = (int) bToken.GetClassNum(tokenStr);
        stopDate = null;
        if (-1 == tClass) {
            Log.e(TAG, "decode: " + "Token Error -- subClass is -1");
            return false;
        } else {
            if (tClass == 0) {
                String key = calcKey(strUuid, androidId);
                if (key != null) {
                    CreditToken myToken = new CreditToken(WorkType.STA, false);
                    if (!myToken.DecodeToken(tokenStr, key)) {
                        Log.e(TAG, "decode: " + "Token Error -- DecodeToken is FALSE");
                        return false;
                    } else {
                        Log.d(TAG, "decode: Amount  : " + myToken.get_amount());
                        Log.d(TAG, "decode: TID     : " + myToken.Tid);
                        Log.d(TAG, "decode: Random  : " + myToken.RandomNum);
                        Log.d(TAG, "decode: DateTime: " + myToken.IssueTime);
                        Log.d(TAG, "decode: subClass: " + myToken.TSubClassNum);

                        stopDate = myToken.IssueTime.plusDays((int) myToken.get_amount());
                        return true;
                    }
                } else {
                    Log.e(TAG, "decode: " + "UUID is error");
                    return false;
                }
            } else {
                Log.e(TAG, "decode: " + "Token Error -- subclass is error");
                return false;
            }
        }
    }

    /*
    * 检查是否允许APP运行，
    * 返回值：
    *   0  : 允许APP运行
    *   1  : 超出使用时间，禁止使用
    *   -1 : 没有UUID信息
    *   -2 : 没有Token信息
    *   -3 : Token解码失败
    *   String android_id = Settings.System.getString(getContentResolver(), Settings.System.ANDROID_ID);
    * */
    public static int isEnable(Context context) throws Exception {
        String sUuid = GuidUtil.createGUID(context);
        if (TextUtils.isEmpty(sUuid)) {
            return  -1;     // 没有UUID
        }
        String sToken = TokenFile.readToken(context);
        if (TextUtils.isEmpty(sToken)) {
            return -2;      // 没有Token
        }

        String android_id = Settings.System.getString(context.getContentResolver(), Settings.System.ANDROID_ID);
        if (decode(sUuid, android_id, sToken)) {
            // 解密token，获得可运行时间
            LocalDateTime nowDate = LocalDateTime.now();
            if (nowDate.isBefore(stopDate)) {
                return 0;   // 始能APP
            } else {
                return 1;   // 超时，禁止APP
            }
        } else {
            return -3;      // token解码失败
        }

    }

    /*
     * 检查是否允许APP运行，
     * 参数：context - 调用者
     *      androidId - 设备自己的 AndroidID 信息
     *                    String android_id = Settings.System.getString(getContentResolver(), Settings.System.ANDROID_ID);
     *      nowTime - 用于检测的时间点
     *
     * 返回值：
     *   0  : 允许APP运行
     *   1  : 超出使用时间，禁止使用
     *   -1 : 没有UUID信息
     *   -2 : 没有Token信息
     *   -3 : Token解码失败
     * */
    public static int isEnable(Context context, LocalDateTime nowTime) throws Exception {
        String sUuid = GuidUtil.createGUID(context);
        Log.e("RunEnable","sUuid: "+sUuid);
        if (TextUtils.isEmpty(sUuid)) {
            return  -1;     // 没有UUID
        }
        String sToken = TokenFile.readToken(context);
        if (TextUtils.isEmpty(sToken)) {
            return -2;      // 没有Token
        }

        String android_id = Settings.System.getString(context.getContentResolver(), Settings.System.ANDROID_ID);
        if (decode(sUuid, android_id, sToken)) {
            // 解密token，获得可运行时间
            if (nowTime.isBefore(stopDate)) {
                return 0;   // 始能APP
            } else {
                Log.e("exit","stopDate:"+stopDate.toString());
                Log.e("exit","nowTime:"+nowTime.toString());
                return 1;   // 超时，禁止APP
            }
        } else {
            Log.e("exit","sToken:"+sToken);
            return -3;      // token解码失败
        }

    }

}
